package com.anlogic.sdk.bootimage.model;

import com.anlogic.sdk.bootimage.model.options.AuthenticationType;
import com.anlogic.sdk.bootimage.model.options.BootgenOptionHelper;
import com.anlogic.sdk.bootimage.model.options.EncryptionType;
import com.anlogic.sdk.bootimage.model.options.IBootgenOption;
import com.anlogic.sdk.bootimage.model.options.MpSoCKeyStore;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.StringUtils;
import com.anlogic.sdk.utils.options.ChipType;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BifReader implements IBifParseConstants {
	private static final String DEFAULT_BOOT_IMAGE_NAME = "BOOT";
	
	private static final Pattern singleCommentPattern = Pattern.compile("//.*\n");
	private static final Pattern multiCommentPattern = Pattern.compile("/\\*[\\s\\S]*\\*/");
	private static final Pattern imageNamePattern = Pattern.compile("\\w+\\s*:\\s*\\{");
	private static final Pattern normalLinePattern = Pattern.compile("\\[[\\s\\S]*\\][\\s\\S]*");
	private static final Pattern bootConfigPattern = Pattern.compile("boot_config\\s*\\{[\\s\\S]*\\}");
	
	
	private String filePath;

	private BootGenModel model;

	private BifReader(String filePath) {
		this.filePath = filePath;
	}

	public static BifReader newInstance(String filePath) {
		return new BifReader(filePath);
	}
	
	public void readBif(BootGenModel model) throws IOException {
		this.model = model;
		if (model.getPartitions() != null)
			model.getPartitions().clear();
		BufferedReader reader = new BufferedReader(new FileReader(this.filePath));
		try {
			String readLine = "";
			int ch = reader.read();
			while (ch != -1) {
				readLine += (char)ch;
				if (normalLinePattern.matcher(readLine).find()) {
					if (readLine.endsWith("[") || readLine.endsWith("}")) {
						readLine = readLine.substring(0, readLine.length()-1).replace("\n", " ").trim();
						processLine(readLine);
						readLine = "";
						readLine += (char)ch;
					} else if (readLine.endsWith("boot_config")) {
						readLine = readLine.split("boot_config")[0].replace("\n", " ").trim();
						processLine(readLine);
						readLine = "boot_config";
					}
				} else if (bootConfigPattern.matcher(readLine).find()) {
					readLine = readLine.replace("\n", " ").trim();
					processBootConfig(readLine);
					readLine = "";
				} else if (singleCommentPattern.matcher(readLine).find()) {
					readLine = readLine.replace("\n", " ").trim();
					processComment(readLine);
					readLine = "";
				} else if (multiCommentPattern.matcher(readLine).find()) {
					readLine = readLine.replace("\n", " ").trim();
					processComment(readLine);
					readLine = "";
				} else if (imageNamePattern.matcher(readLine).find()) {
					readLine = "";
				}
				ch = reader.read();
			}
		} finally {
			reader.close();
		}
	}

	private void processComment(String readLine) {
		List<String> fieldEntries = null; 
		if (readLine.startsWith("//")) {
			readLine = readLine.substring("//".length());
			fieldEntries = convertToFieldEntries(readLine);
		}
		if (readLine.startsWith("/*") && readLine.endsWith("*/")) {
			readLine = readLine.split("/\\*")[1];
			readLine = readLine.split("\\*/")[0];
			readLine = readLine.trim();
			fieldEntries = convertToFieldEntries(readLine);
		}
		
		for (String fieldEntry : fieldEntries) {
			String[] split = fieldEntry.split("=");
			if (split.length == 2) {
				String key = split[0].trim();
				String value = split[1].trim();
				if ("arch".equals(key)) {
					this.model.setChipType(ChipType.getTypeByName(value));
					continue;
				}
				if ("format".equals(key)) {
					this.model.setFormatType(value);
					continue;
				}
//				if ("split".equals(key)) {
//					this.model.setSplit(Boolean.valueOf(value).booleanValue());
//					continue;
//				}
//				if ("key_part_name".equals(key)) {
//					this.model.setPartName(value);
//					this.model.setUseEncryption(true);
//				}
			}
		}
	}
	
	private void processBootConfig(String readLine) {
		if (readLine.contains("boot_config")) {
			readLine = readLine.substring(readLine.indexOf("{")+1, readLine.indexOf("}"));
			List<String> fieldEntries = convertToBootConfigFieldEntries(readLine);
			for (String fieldEntry : fieldEntries) {
				String[] split = fieldEntry.split("=");
				if (split.length == 2) {
					String property = split[0].trim();
					String value = split[1].trim();
					if ("authentication".equals(property)) {
						this.model.setUseAuthentication(true);
						this.model.setAuthType(AuthenticationType.valueOf(value.toUpperCase()));
						continue;
					}
				} else if (split.length == 1) {
					String property = split[0].trim();
					if ("bh_auth_enable".equals(property)) {
						this.model.setBHAuth(true);
						this.model.setUseAuthentication(true);
						continue;
					}
					if ("qspi_x1_only".equals(property)) {
						this.model.setQspiX1(true);
						this.model.setUseAuthentication(true);
						continue;
					}
					if ("mmc_clk_sel".equals(property)) {
						this.model.setMmcClk(true);
						this.model.setUseAuthentication(true);
						continue;
					}
				}
			}
		}
	}

	public static String computeOutImagePath(BootGenModel model) {
		String outImagePath = null;
		String bifPath = model.getBifFile();
		String formatType = model.getFormatType();
		if (StringUtils.isNotEmpty(model.getOutputFile())) {
			File exitingOutputFile = new File(model.getOutputFile());
			String fileNameWithoutExt = FileUtils.getFileNameWithoutExt(exitingOutputFile.getName());
			String fileName = String.valueOf(fileNameWithoutExt) + "." + formatType.toLowerCase();
			outImagePath = (new File(exitingOutputFile.getParentFile(), fileName)).getAbsolutePath();
		} else if (StringUtils.isNotEmpty(bifPath)) {
			File bifDirectiory = (new File(bifPath)).getParentFile();
			if (model.isSplit()) {
				outImagePath = bifDirectiory.getAbsolutePath();
			} else {
				String imageFileName = getDefaultBootImageFileName(formatType);
				outImagePath = (new File(bifDirectiory, imageFileName)).getAbsolutePath();
			}
		}
		return outImagePath;
	}

	public static String getDefaultBootImageFileName(String formatType) {
		return DEFAULT_BOOT_IMAGE_NAME + "." + formatType.toLowerCase();
	}

	private static Pattern KEYLINE_PATTERN = Pattern.compile(".*\\[(.*)\\](.*)");

	private void processLine(String line) {
		Matcher keyLineMatcher = KEYLINE_PATTERN.matcher(line);
		if (keyLineMatcher.matches()) {
			String param = keyLineMatcher.group(1).trim();
			String value = keyLineMatcher.group(2).trim();
			if (BifWriter.getSupportedkeys().contains(param)) {
				processEntry(param, value);
				return;
			}
		}
		Partition partition = PartitionStringConverter.convertToPartition(line, this.model.getChipType());
		this.model.addPartition(partition);
		processPartitionConfig(partition);
	}

	private void processPartitionConfig(Partition partition) {
		if(!partition.getAuthenticationType() .equals(AuthenticationType.NONE)) {
			this.model.setAuthType(partition.getAuthenticationType());
		}
		if(!partition.getEncryptionType().equals(EncryptionType.NONE)) {
			this.model.setEncryptionType(partition.getEncryptionType());
		}
		if(!partition.getAESKeyFile().isEmpty()) {
			this.model.setKeyFile(partition.getAESKeyFile());
		}
		if(!partition.getSM4KeyFile().isEmpty()) {
			this.model.setKeyFile(partition.getSM4KeyFile());
		}
	}
	
	private void processEntry(String param, String value) {
		if ("keysrc_encryption".equals(param)) {
			this.model.setMpSoCKeyStore((MpSoCKeyStore) BootgenOptionHelper
					.getNonNullById((IBootgenOption[]) MpSoCKeyStore.values(), value));
			this.model.setUseEncryption(true);
		} else if ("ppkfile".equals(param)) {
			this.model.setPpkFile(value);
			this.model.setUseAuthentication(true);
		} else if ("pskfile".equals(param)) {
			this.model.setPskFile(value);
			this.model.setUseAuthentication(true);
		} else if ("spkfile".equals(param)) {
			this.model.setSpkFile(value);
			this.model.setUseAuthentication(true);
		} else if ("sskfile".equals(param)) {
			this.model.setSskFile(value);
			this.model.setUseAuthentication(true);
		} else if ("spksignature".equals(param)) {
			this.model.setSpkSignFile(value);
			this.model.setUseAuthentication(true);
		} else if ("auth_params".equals(param)) {
			processAuthParam(value);
			this.model.setUseAuthentication(true);
		}
	}

	private void processAuthParam(String fieldsString) {
		List<String> fieldEntries = convertToFieldEntries(fieldsString);
		for (String fieldEntry : fieldEntries) {
			String[] split = fieldEntry.split("=");
			if (split.length == 2) {
				String fieldKey = split[0].trim();
				String value = split[1].trim();
				if ("spk_id".equals(fieldKey))
					this.model.setSpkId(value);
			}
		}
	}

	private List<String> convertToFieldEntries(String fieldsString) {
		return StringUtils.convertToList(fieldsString, ";");
	}
	
	private List<String> convertToBootConfigFieldEntries(String fieldsString) {
		return StringUtils.convertToList(fieldsString, ",");
	}
}
